package plp.funcoo.expressions;

import plp.funcoo.environment.compilation.CompilationEnvironment;
import plp.funcoo.environment.compilation.Environment;
import plp.funcoo.environment.execution.ExecutionContext;
import plp.funcoo.environment.execution.ExecutionEnvironment;
import plp.funcoo.exceptions.ClassAlreadyDeclared;
import plp.funcoo.exceptions.ClassNotDeclared;
import plp.funcoo.exceptions.MethodAlreadDeclared;
import plp.funcoo.exceptions.MethodNotDeclared;
import plp.funcoo.exceptions.ParameterNumberIncorrect;
import plp.funcoo.exceptions.ParameterTypeIncorrect;
import plp.funcoo.exceptions.VarAlreadyDeclared;
import plp.funcoo.exceptions.VarNotDeclared;
import plp.funcoo.types.PrimitiveType;
import plp.funcoo.util.Type;
import plp.funcoo.value.VoidValue;

public class BlockExpression implements Expression {

	private ExpressionList expressionList;
	
	public BlockExpression()
	{
		
	}
	public BlockExpression(ExpressionList expressionList)
	{
				
		this.expressionList = expressionList;
	}
	
	@Override
	public ExecutionEnvironment evaluate(ExecutionEnvironment envi) throws VarNotDeclared, MethodNotDeclared, VarAlreadyDeclared, MethodAlreadDeclared {
		
		envi.increase();
		 ((ExecutionContext)envi).cleanExpression();
		ExpressionList expressionList = this.expressionList.makeCopy();
		while(isVoid(((ExecutionContext)envi)) && expressionList.length() > 0 )
		{
			envi = expressionList.getLast().evaluate(envi);
		}
		envi.restore();
		return envi;
	}
	
	private boolean isVoid(ExecutionContext envi)
	{
		boolean result = true;
		
		try {
			result = ((envi.getValueAnalyzed()).getType(envi)).isVoid();
		} catch (VarNotDeclared | ClassNotDeclared e) {
		
			e.printStackTrace();
		}
		return result;
	}

	@Override
	public boolean typeCheck(CompilationEnvironment envi)
			throws VarAlreadyDeclared, VarNotDeclared, ClassAlreadyDeclared,
			ClassNotDeclared, MethodAlreadDeclared, MethodNotDeclared, ParameterTypeIncorrect, ParameterNumberIncorrect {
		
		envi.increase();
		boolean result = this.expressionList.typeCheck(envi);
		envi.restore();
		return result;
	}

	@Override
	public Type getType(Environment envi) throws VarNotDeclared,
			ClassNotDeclared {
		
		Type type = PrimitiveType.Type_VOID;
		ExpressionList expressionList = (ExpressionList) this.expressionList.makeCopy();
		while(type.isVoid() && expressionList.length() > 0 )
		{
			type = expressionList.getLast().getType(envi);
		}
		
		return type;
	}
	@Override
	public Expression makeCopy() {
		
		return new BlockExpression((ExpressionList) this.expressionList.makeCopy());
	}

	
}
